#include <iostream>
#include <vector>
#include <unordered_map>
#include <cstring>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "task.hpp"

using namespace std;

class CtlProcess
{
private:
    int _num;                     // 管道的数量，子进程的数量
    unordered_map<int, pair<pid_t,int>> hash; // 建立子进程编号与 <pid_t,写端>的关系
    vector<int> parent_write_fds; // 父进程写端文件描述符集合
    vector<Task::func> tasks; // 任务
public:
    CtlProcess(int num = 3)
    :_num(num)
    {
        // 加载任务
        tasks.push_back(bind(&Task::SyncLogTask,Task()));
        tasks.push_back(bind(&Task::MySqlTask,Task()));
        tasks.push_back(bind(&Task::NetWork,Task()));
    }

    // 创建模型
    void CreateModel()
    {
        for (int i = 0; i < _num; i++)
        {
            int fds[2];
            if (pipe(fds) == -1)
            {
                cout << "匿名管道创建失败" << endl;
                exit(-1);
            }
            pid_t pid = fork();
            if (pid == 0) // 子进程
            {
                cout << getpid() << ": 子进程创建成功" << endl;
                // 关闭继承下来的 父进程写端
                for (auto &fd : parent_write_fds)
                    close(fd);
                // 这是因为进程的独立性,所以要把这一次的写端关闭
                close(fds[1]);

                // 正常来讲,子进程的读端都应该是3号，但是为了方便起见，我们可以一个重定向
                // 让0号文件描述符 变成管道的读到
                // oldfd newfd
                // 现在让0号fd作为新的读端
                dup2(fds[0], 0);
                // 执行子进程的功能
                ReadPipe(i);
                exit(0);
            }
            // 建立写端和子进程编号的对应关系
            hash[i] = make_pair(pid,fds[1]);
            // 父进程
            // 关闭读端
            close(fds[0]);
            // 记录写端，让子进程关闭
            parent_write_fds.push_back(fds[1]);
        }
    }

    // 父进程进行控制
    void Centre()
    {
        int cnt = 0;
        bool running = true;
        while(running)
        {
            // 遍历子进程编号，找到对应的写端
            for(int i = 0; i < _num; i++)
            {
                int write_fd = hash[i].second;
                char buf[128];
                // 6秒内执行任务
                if(cnt < 6)
                    snprintf(buf,sizeof(buf),"%d,子进程-%d: 执行%d任务\n",i,i,i);
                else // 超过六秒退出任务
                    snprintf(buf,sizeof(buf),"%d,子进程-%d: 执行%d任务\n",4,i,i);
                int n = write(write_fd,buf,sizeof(buf));
                sleep(1);
                cnt++;
                if(cnt > 6)
                    running = false;
            }
        }
    }

    // 控制子进程：子进程读取管道
    void ReadPipe(int num)
    {
        while (true)
        {
            char buf[128];
            // 因为进行了重定向，此时子进程的读端是0号文件描述符
            int n = read(0, buf, sizeof(buf));
            if(n == 0)
            {
                cout << "写端关闭，我也关闭..." << endl;
                close(0);
                break;
            }
            else if(n == -1)
            {
                printf("%d-%s",errno,strerror(errno));
                
                exit(-1);
            }
            else
            {
                cout << "我是子进程-" << num << "我从管道中获取到了：" << buf << endl;
                int command = buf[0]-'0';
                // 执行对应编号的任务
                if(command != 4)
                    tasks[command]();
                else
                {
                    cout << "运行时间到了，我子进程要退出了..." << endl;
                    exit(0);
                }
            }
            
        }
    }


    // 父进程等待子进程退出
    void WaitChild()
    {
        int cnt = 0;
        bool running = true;
        while(running)
        {
            for(auto& e : hash)
            {
                sleep(1);
                pid_t pid_c = e.second.first;
                cout << "我是父进程，我要等待pid_c = " << pid_c << endl;
                // 非阻塞轮询
                pid_t pid = waitpid(pid_c,NULL,WNOHANG);
                if(pid > 0)
                {
                    cnt++;
                    cout << "pid = " << pid << "等待成功,并关闭文件描述符 " <<  endl;
                    close(e.second.second);
                }
                else if(pid == -1)
                {
                    cout << "等待失败 " << errno << " " << strerror(errno) << endl;
                }
                if(cnt == hash.size())
                   running = false;

            }
        }
        cout << "等待成功" << endl;

    }
};